package cn.jw.starter.jwt.core;

import java.time.Duration;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.alibaba.fastjson2.JSON;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;

/**
 * @author 王广帅
 * @since 2023/5/23 22:27
 **/
@Slf4j
public class JwtTokenService<T extends JwBaseTokenObj> {

    private final static String JWT_SUBJECT = "game_token";
    private final static String TOKEN_EXTRA_KEY = "token_extra_key";

    /**
     * 创建一个Jwt token
     *
     * @param data
     *            需要携带的业务数据，这里不要放置敏感信息，因为它是明文传输的
     * @param expire
     *            token的有效时间
     * @param key
     *            HS512的签名密钥
     * @return
     */
    public String createJwtToken(@Valid T data, Duration expire, byte[] key) {
        Date expireDate = new Date(System.currentTimeMillis() + expire.toMillis());
        JwtBuilder jwtBuilder = Jwts.builder().setSubject(JWT_SUBJECT);
        if (data != null) {
            Map<String, Object> claims = new HashMap<>();
            claims.put(TOKEN_EXTRA_KEY, JSON.toJSONString(data));
            jwtBuilder.addClaims(claims);
        }
        String token = jwtBuilder.setExpiration(expireDate).compressWith(CompressionCodecs.DEFLATE)
            .signWith(SignatureAlgorithm.HS512, key).compact();
        return token;
    }

    /**
     * 检查 jwt token并获取token携带的业务数据
     *
     * @param token
     * @param key
     * @return
     * @throws JwtTokenExpiredException
     * @throws JwtTokenErrorException
     */
    public T checkTokenAndGet(String token, byte[] key, Class<T> clazz)
        throws JwtTokenExpiredException, JwtTokenErrorException {
        try {
            Jws<Claims> headerClaimsJwt =
                Jwts.parser().requireSubject(JWT_SUBJECT).setSigningKey(key).build().parseSignedClaims(token);
            Claims body = headerClaimsJwt.getBody();
            String value = (String)body.get(TOKEN_EXTRA_KEY);
            return JSON.parseObject(value, clazz);
        } catch (ExpiredJwtException e) {
            throw new JwtTokenExpiredException("token 已过期," + token);
        } catch (Throwable e) {
            if (log.isDebugEnabled()) {
                log.debug("token解析失败", e);
            }
            throw new JwtTokenErrorException("token不合法," + token);
        }
    }
}
